//
//  IRController.m
//

#import "IRController.h"

#import "AppleRemote.h"
#import "Controller.h"
#import "FullScreenWindow.h"
#import "IRCaptionView.h"
#import "IRMenuView.h"
#import "IRSelectionView.h"
#import "PhotoSlideShow.h"
#import "PhotoViewer.h"
#import "PreferencesData.h"
#import "SlideshowManager.h"
#import "SlideshowModel.h"
#import "Utility.h"


@implementation IRController


static IRController*	instance = nil;

//--------------------------------------------------------------------------------------------------
+ (IRController*) sharedController
{
	if (nil == instance)
	{
		instance = [[IRController alloc] init];
	}

	return instance;
}

//--------------------------------------------------------------------------------------------------
- (void) startTopLevel
{
	if (nil != mainWindow)
	{
		return;
	}

	menuFont = [[NSFontManager sharedFontManager] 
			fontWithFamily:@"Verdana"
			traits:NSBoldFontMask
			weight:15
			size:64];

	menuAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
		menuFont, NSFontAttributeName,
		[NSColor whiteColor], NSForegroundColorAttributeName,
		nil];

	fontHeight = [menuFont ascender] - [menuFont descender];
	lineHeight = fontHeight * 1.25;

	// Get into full screen mode; add the caption view to the full screen window
	NSRect screenRect = [[NSScreen mainScreen] frame];
	mainWindow = [[FullScreenWindow alloc] 
		initWithContentRect: screenRect
		styleMask: NSBorderlessWindowMask
		backing: NSBackingStoreBuffered
		defer: NO];

	[mainWindow useOptimizedDrawing: YES];
	[mainWindow setDelegate: self];
	[mainWindow setBackgroundColor: [NSColor colorWithDeviceRed: 0.1 green: 0.1 blue: 0.1 alpha: 1.0]];

	captionView = [[IRCaptionView alloc] initWithFrame: screenRect];
	[captionView setCaption: @"GoFoto"];
	[mainWindow setContentView: captionView];


	int separator = [self separator];
	NSRect rectMenu = NSMakeRect(
		screenRect.size.width * .25, 
		0, 
		screenRect.size.width *.75 - 30, 
		separator);
	menuView = [[IRMenuView alloc] initWithFrame: rectMenu];


	// Selection view, in an overlay window below the menu view (the window is created earlier,
	// causing it to be below the menu window)
	int yOffset = separator;
	int xOffset = screenRect.size.width * .25;
	int x = xOffset - 60;
	int y = yOffset - (([menuView middleSlot] + 1) * lineHeight - [menuFont descender]);

	NSRect rectSel = NSMakeRect(x, y, screenRect.size.width - x - 30, lineHeight);
	selectionWindow = [[NSWindow alloc]
		initWithContentRect: rectSel
		styleMask: NSBorderlessWindowMask 
		backing: NSBackingStoreBuffered 
		defer: YES];

	selectionView = [[IRSelectionView alloc] initWithFrame: rectSel];
	[selectionWindow setContentView: selectionView];
	NSPoint origin = screenRect.origin;
	origin.x += x;
	origin.y += y;

	[selectionWindow setLevel: NSFloatingWindowLevel];
	[selectionWindow setOpaque: NO];
	[selectionWindow setAlphaValue: 1.0];
	[selectionWindow setBackgroundColor: [NSColor clearColor]];
	[selectionWindow setFrameOrigin: origin];

	[selectionWindow orderFront: self];
	[mainWindow addChildWindow: selectionWindow  ordered: NSWindowAbove];
	[selectionView setNeedsDisplay: YES];


	// Menu view, owned by an overlay window sitting below the caption
	menuWindow = [[NSWindow alloc]
		initWithContentRect: rectMenu
		styleMask: NSBorderlessWindowMask 
		backing: NSBackingStoreBuffered 
		defer: YES];
	[menuView setItems: topLevelMenu];
	[menuWindow setContentView: menuView];

	origin = screenRect.origin;
	origin.x += rectMenu.origin.x;
	origin.y += rectMenu.origin.y;
	[menuWindow setLevel: NSFloatingWindowLevel];
	[menuWindow setOpaque: NO];
	[menuWindow setAlphaValue: 1.0];
	[menuWindow setBackgroundColor: [NSColor clearColor]];
	[menuWindow setFrameOrigin: origin];

	[menuWindow orderFront: self];
	[mainWindow addChildWindow: menuWindow  ordered: NSWindowAbove];



	HideMenuBar();

	[mainWindow makeFirstResponder: mainWindow];
	[mainWindow makeKeyAndOrderFront: mainWindow];


	activeObject = kActiveRemote_MainMenu;

	btnDown = -1;
	menuTimer = [NSTimer 
		scheduledTimerWithTimeInterval:0.01
		target:self 
		selector:@selector(menuTimer:) 
		userInfo:nil 
		repeats:YES];
}

//--------------------------------------------------------------------------------------------------
- (void) cleanup
{
	// Get out of full screen mode.
	ShowMenuBar();

	[mainWindow orderOut: self];
	[mainWindow close];
	mainWindow = nil;

	[menuTimer invalidate];
	menuTimer = nil;

	menuView = nil;
}

//--------------------------------------------------------------------------------------------------
- (id) init
{
	if (self = [super init])
	{
		if (YES == [[[Controller sharedController] preferencesData] useAppleRemote])
		{
			AppleRemote *remote = [AppleRemote sharedRemote];
			[remote setDelegate: self];
			if ([remote isRemoteAvailable])
			{
				[remote setOpenInExclusiveMode: YES];
				[remote startListening: self];
			}
		}

		IRMenuItem *folders = [[IRMenuItem alloc] initWithName: @"Folders"  hasChildren:YES  obj:nil];
		IRMenuItem *ssMI = [[IRMenuItem alloc] initWithName: @"Slideshows"  hasChildren:YES  obj:nil];
		topLevelMenu = [NSArray arrayWithObjects: folders, ssMI, nil];
		[topLevelMenu retain];

		menuTimer = nil;
	}

	return self;
}

//--------------------------------------------------------------------------------------------------
- (NSFont*) menuFont
{
	return menuFont;
}

//--------------------------------------------------------------------------------------------------
- (NSDictionary*) menuAttributes
{
	return menuAttributes;
}

//--------------------------------------------------------------------------------------------------
- (int) lineHeight
{
	return lineHeight;
}

//--------------------------------------------------------------------------------------------------
- (int) fontHeight
{
	return fontHeight;
}

//--------------------------------------------------------------------------------------------------
- (int) separator
{
	return [mainWindow frame].size.height - (2 * fontHeight);
}

//--------------------------------------------------------------------------------------------------
NSString* resolveFinderAlias(NSString* path)
{
	NSString *resolvedPath = nil;
	CFURLRef url = CFURLCreateWithFileSystemPath(
		NULL, 
		(CFStringRef)path,
		kCFURLPOSIXPathStyle, 
		NO);

	if (url != NULL)
	{
		FSRef fsRef;
		if (CFURLGetFSRef(url, &fsRef))
		{
			Boolean targetIsFolder, wasAliased;
			if (noErr == FSResolveAliasFile (&fsRef, true, &targetIsFolder, &wasAliased) 
				&& wasAliased)
			{
				CFURLRef resolvedUrl = CFURLCreateFromFSRef(NULL, &fsRef);
				if (NULL != resolvedUrl)
				{
					resolvedPath = (NSString*)CFURLCopyFileSystemPath(resolvedUrl,kCFURLPOSIXPathStyle);
					CFRelease(resolvedUrl);
				}
			}
		}
		else
		{
			// Probably means there's an alias in the middle of the path...
			// So recurse back through the components until we hit on the alias bit - resolve that 
			// and then tack the components back on.
			NSString* lastPathComponent = [path lastPathComponent];
			resolvedPath = resolveFinderAlias([path stringByDeletingLastPathComponent]);
			resolvedPath = [resolvedPath stringByAppendingPathComponent: lastPathComponent];
		}

		CFRelease(url);
	}
 
	if (nil == resolvedPath)
	{
		resolvedPath = [[[NSString alloc] initWithString:path] autorelease];
	}

	return resolvedPath;
}

//--------------------------------------------------------------------------------------------------
CFComparisonResult sortMenuItemsByName(const void *val1, const void *val2, void *context)
{
	IRMenuItem *item1 = (IRMenuItem*) val1;
	IRMenuItem *item2 = (IRMenuItem*) val2;

	NSString *str1 = [item1 name];
	NSString *str2 = [item2 name];

	return [str1 compare: str2  options: NSCaseInsensitiveSearch];
}

//--------------------------------------------------------------------------------------------------
- (BOOL) setMenuToFolder:(NSString*) folderPath
{
	NSMutableArray *menuFolders = [NSMutableArray arrayWithCapacity: 0];
	NSMutableArray *menuFiles = [NSMutableArray arrayWithCapacity: 0];
	Controller *controller = [Controller sharedController];

	NSString *resolvedPath = resolveFinderAlias(folderPath);

	BOOL succeeded = NO;


	FSRef container;
	OSErr err = FSPathMakeRef((const UInt8 *) [resolvedPath UTF8String], &container, NULL);
	if (noErr != err)
	{
		NSLog(@"FSPathMakeRef failed with %d (on %@)", err, resolvedPath);
		return NO;
	}
	else
	{
		FSIterator iterator;
		err = FSOpenIterator(&container, kFSIterateFlat, &iterator);
		if (noErr != err)
		{
			NSLog(@"FSOpenIterator failed with %d (on %@)", err, resolvedPath);
		}
		else
		{
			// Use up to four pages of memory
			ItemCount maxObjects = ((4096 * 4) / sizeof(FSCatalogInfo));
			FSCatalogInfo *catalogInfo = malloc(maxObjects * sizeof(FSCatalogInfo));
			FSRef *refs = malloc(maxObjects * sizeof(FSRef));

			do
			{
				ItemCount numObjects = 0;
				err = FSGetCatalogInfoBulk(
						iterator, 
						maxObjects, 
						&numObjects,
						NULL, 
						kFSCatInfoContentMod | kFSCatInfoNodeFlags | kFSCatInfoFinderInfo, 
						catalogInfo,
						refs, 
						NULL, 
						NULL);

				if (noErr == err || errFSNoMoreItems == err)
				{
					int idx;
					for (idx = 0; idx < numObjects; ++idx)
					{
						FSCatalogInfo *itemInfo = &(catalogInfo[idx]);

						UInt8 uintName[PATH_MAX + 1];
						FSRefMakePath (&refs[idx], uintName, PATH_MAX);
						NSString *fullPath = [NSString stringWithUTF8String: (const char *)uintName];

						NSString *name = [fullPath lastPathComponent];
						BOOL isFolder = kFSNodeIsDirectoryMask == (itemInfo->nodeFlags & kFSNodeIsDirectoryMask);
						if (NO == isFolder)
						{
							FileInfo *fInfo = (FileInfo*) &itemInfo->finderInfo;
							isFolder = kIsAlias == (fInfo->finderFlags & kIsAlias);
						}

						if (isFolder)
						{
							CFMutableArrayRef cfList = (CFMutableArrayRef) menuFolders;
							CFRange range = CFRangeMake(0, [menuFolders count]);
							IRMenuItem *mi = [[IRMenuItem alloc] initWithName:name  hasChildren:YES  obj:nil];
							int index = CFArrayBSearchValues(
								cfList, 
								range, 
								mi, 
								sortMenuItemsByName, 
								nil);

							[menuFolders insertObject:mi  atIndex:index];
							[mi release];
						}
						else
						if (YES == [controller isSupportedFile: fullPath])
						{
							CFMutableArrayRef cfList = (CFMutableArrayRef) menuFiles;
							CFRange range = CFRangeMake(0, [menuFiles count]);
							int index = CFArrayBSearchValues(
								cfList, 
								range, 
								name, 
								(CFComparatorFunction)CFStringCompare, 
								(void*)(kCFCompareCaseInsensitive | kCFCompareNumerically));

							[menuFiles insertObject:name  atIndex:index];
						}
					}
				}

			} while (noErr == err);

			if (errFSNoMoreItems == err)
			{
				err = noErr;
				succeeded = YES;
			}

			free(refs);
			free(catalogInfo);
			FSCloseIterator(iterator);
		}
	}

	[firstImage release];
	firstImage = nil;
	NSMutableArray *menu = [NSMutableArray arrayWithArray: menuFolders];
	if ([menuFiles count] > 0)
	{
		NSString *play = [NSString stringWithFormat: @"Play (%d)", [menuFiles count]];
		IRMenuItem *mi = [[IRMenuItem alloc] initWithName:play  hasChildren:NO  obj:nil];
		[menu insertObject:mi  atIndex: 0];
		[mi release];

		zeroIndexIsPlay = YES;
		firstImage = [menuFiles objectAtIndex: 0];
		[firstImage retain];
	}
	else
	{
		zeroIndexIsPlay = NO;
	}

	[menuView setItems: menu];


	if (noErr != err)
	{
		NSLog(@"IRController.setMenuToFolder failed with %d (on %@)", err, resolvedPath);
	}
	else
	{
		[curFolder release];
		curFolder = folderPath;
		[curFolder retain];
	}

	return succeeded;
}

//--------------------------------------------------------------------------------------------------
- (void) upOneMenuLevel
{
	NSString *selectedItem = nil;

	switch (activeObject)
	{
		case kActiveRemote_ImageViewer:
			[mainWindow makeKeyAndOrderFront: nil];
			[[activeSlideshow mainWindow] close];
			activeSlideshow = nil;
			activeObject = kActiveRemote_FoldersMenu;
			break;

		case kActiveRemote_FoldersMenu:
			if (NSOrderedSame == [curFolder compare: GetPicturesFolder()])
			{
				activeObject = kActiveRemote_MainMenu;
				selectedItem = @"Folders";
			}
			else
			{
				NSString *lastComponent = [curFolder lastPathComponent];
				[self setMenuToFolder: [curFolder stringByDeletingLastPathComponent]];
				[menuView setSelectedItem: lastComponent];
			}
			break;

		case kActiveRemote_SlideshowsMenu:
			selectedItem = @"Slideshows";
			activeObject = kActiveRemote_MainMenu;
			break;

		case kActiveRemote_SlideshowViewer:
			[[activeSlideshow mainWindow] close];
			activeSlideshow = nil;
			activeObject = kActiveRemote_SlideshowsMenu;
			break;

		case kActiveRemote_MainMenu:
		default:
			[self cleanup];
			return;
			break;
	}

	if (kActiveRemote_MainMenu == activeObject)
	{
		[captionView setCaption: @"GoFoto"];
		[menuView setItems: topLevelMenu];
		if (nil != selectedItem)
		{
			[menuView setSelectedItem: selectedItem];
		}
	}
}

//--------------------------------------------------------------------------------------------------
- (void) openSelectedFolder:(int) index  name:(NSString*) name
{
	if (0 == index && YES == zeroIndexIsPlay)
	{
		NSString *fullPath = resolveFinderAlias(curFolder);

		SlideshowModel *model = [[SlideshowModel alloc] initWithDirectory: fullPath  name:@"" incSub:NO];
		[model setSortBy: SS_SORT_DATE];
		activeSlideshow = [[Controller sharedController] openSlideshow: model   show:NO];
		[[activeSlideshow mainWindow] setFrame: [mainWindow frame]  display:NO];
		[activeSlideshow toggleFullScreen: nil];
		[model release];
		activeObject = kActiveRemote_ImageViewer;
		return;
	}

	[self setMenuToFolder: [curFolder stringByAppendingPathComponent: name]];
}

//--------------------------------------------------------------------------------------------------
- (void) openSelectedSlideshow:(int) index
{
	SlideshowModel *model = [slideshows objectAtIndex: index];
	activeSlideshow = [[Controller sharedController] openSlideshow: model   show:NO];
	[[activeSlideshow mainWindow] setFrame: [mainWindow frame]  display:NO];
	[activeSlideshow toggleFullScreen: nil];
	activeObject = kActiveRemote_SlideshowViewer;
}

//--------------------------------------------------------------------------------------------------
- (void) openSelectedItem
{
	IRMenuItem *selMenu = [menuView selectedItem];
	NSString *selItem = [selMenu name];

	switch (activeObject)
	{
		case kActiveRemote_MainMenu:
			if (NSOrderedSame == [@"Folders" caseInsensitiveCompare: selItem])
			{
				[captionView setCaption: @"Folders"];
				[self setMenuToFolder: GetPicturesFolder()];
				activeObject = kActiveRemote_FoldersMenu;
			}
			else
			if (NSOrderedSame == [@"Slideshows" caseInsensitiveCompare: selItem])
			{
				[slideshows release];
				slideshows = [[[[SlideshowManager alloc] init] autorelease] enumSlideshows];
				[slideshows retain];
				activeObject = kActiveRemote_SlideshowsMenu;
				NSMutableArray *items = [[NSMutableArray alloc] initWithCapacity: [slideshows count]];

				NSEnumerator *iter = [slideshows objectEnumerator];
				id obj;
				while (obj = [iter nextObject])
				{
					SlideshowModel *ssModel = obj;
					IRMenuItem *mi = [[IRMenuItem alloc] initWithName:[ssModel name]  hasChildren:NO obj:nil];
					[items addObject: mi];
					[mi release];
				}

				[captionView setCaption: @"Slideshows"];
				[menuView setItems: items];
				[items release];
				activeObject = kActiveRemote_SlideshowsMenu;
			}
			break;

		case kActiveRemote_FoldersMenu:
			[self openSelectedFolder: [menuView selectedIndex]  name: selItem];
			break;

		case kActiveRemote_SlideshowsMenu:
			[self openSelectedSlideshow: [menuView selectedIndex]];
			break;

		case kActiveRemote_ImageViewer:
		case kActiveRemote_SlideshowViewer:
			[activeSlideshow toggleAutoShow: nil];
			break;

		default:
			break;
	}
}

//--------------------------------------------------------------------------------------------------
- (void) moveLeft
{
	switch (activeObject)
	{
		case kActiveRemote_SlideshowViewer:
		case kActiveRemote_ImageViewer:
			[activeSlideshow previousImage: nil];
			break;
	}
}

//--------------------------------------------------------------------------------------------------
- (void) moveRight
{
	switch (activeObject)
	{
		case kActiveRemote_SlideshowViewer:
		case kActiveRemote_ImageViewer:
			[activeSlideshow nextImage: nil];
			break;
	}
}

//--------------------------------------------------------------------------------------------------
- (void) appleRemoteButton:(AppleRemoteEventIdentifier) ident  pressedDown:(BOOL) down
{
	if (YES == down)
	{
		if (btnDown != ident)
		{
			ticksStart = 50;
			ticksLeft = ticksStart;
		}

		btnDown = ident;
	}
	else
	{
		btnDown = -1;
	}

	switch (ident)
	{
		case kRemoteButtonVolume_Plus:
			if (nil != mainWindow)
			{
				if (YES == down)
				{
					[menuView selectPrevious];
				}
			}
			break;

		case kRemoteButtonVolume_Minus:
			if (nil != mainWindow)
			{
				if (YES == down)
				{
					[menuView selectNext];
				}
			}
			break;

		case kRemoteButtonPlay:
			if (nil != mainWindow && YES == down)
			{
				[self openSelectedItem];
			}
			break;

		case kRemoteButtonMenu:
			if (nil == mainWindow)
			{
				[self startTopLevel];
			}
			else
			{
				[self upOneMenuLevel];
			}
			break;

		case kRemoteButtonLeft:
			if (nil != mainWindow)
			{
				[self moveLeft];
			}
			break;

		case kRemoteButtonRight:
			if (nil != mainWindow)
			{
				[self moveRight];
			}
			break;

		default:
//NSLog(@"identifier %d; down %d", ident, down);
			break;
	}

}

//--------------------------------------------------------------------------------------------------
- (void) menuTimer:(NSTimer*) timer
{
	if (btnDown == kRemoteButtonVolume_Minus ||
		btnDown == kRemoteButtonVolume_Plus)
	{
		if (0 == --ticksLeft)
		{
			[self appleRemoteButton:btnDown  pressedDown:YES];
			ticksStart -= 10;
			if (ticksStart < 1)
			{
				ticksStart = 1;
			}
			ticksLeft = ticksStart;
		}
	}
}

@end
